﻿/*
 * Copyright (C) 2014 Cypress Data Defense, LLC    
 * oss@cypressdefense.com
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
*/

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using CypressDefense.Security.Shim.Configuration.Base;

namespace CypressDefense.Security.Shim.Configuration
{

    public class ShimConfiguration : System.Configuration.ConfigurationSection
    {
        #region Private Variables
        public const string _RELATIVE_PATH_KEY = "/";
        #endregion

        #region Properties
        [ConfigurationProperty("enabled", DefaultValue = true, IsRequired = false)]
        public bool Enabled
        {
            get
            {
                return (bool)this["enabled"];
            }
            set
            {
                this["enabled"] = value;
            }
        }

        [ConfigurationProperty("caching", IsRequired = false)]
        public Caching.CachingElement Caching
        {
            get
            {
                return (Caching.CachingElement)this["caching"];
            }
            set
            {
                this["caching"] = value;
            }
        }

        [ConfigurationProperty("contentSecurityPolicy", IsRequired = false)]
        public Csp.CspElement ContentSecurityPolicy
        {
            get
            {
                return (Csp.CspElement)this["contentSecurityPolicy"];
            }
            set
            {
                this["contentSecurityPolicy"] = value;
            }
        }

        [ConfigurationProperty("strictTransportSecurity", IsRequired = false)]
        public Hsts.StrictTransportSecurityElement StrictTransportSecurity
        {
            get
            {
                return (Hsts.StrictTransportSecurityElement)this["strictTransportSecurity"];
            }
            set
            {
                this["strictTransportSecurity"] = value;
            }
        }

        [ConfigurationProperty("xContentTypeOptions", IsRequired = false)]
        public XContentTypeOptions.XContentTypeOptionsElement XContentTypeOptions
        {
            get
            {
                return (XContentTypeOptions.XContentTypeOptionsElement)this["xContentTypeOptions"];
            }
            set
            {
                this["xContentTypeOptions"] = value;
            }
        }

        [ConfigurationProperty("xFrameOptions", IsRequired = false)]
        public XFrameOptions.XFrameOptionsElement XFrameOptions
        {
            get
            {
                return (XFrameOptions.XFrameOptionsElement)this["xFrameOptions"];
            }
            set
            {
                this["xFrameOptions"] = value;
            }
        }

        [ConfigurationProperty("xXssProtection", IsRequired = false)]
        public XXssProtection.XXssProtectionElement XXssProtection
        {
            get
            {
                return (XXssProtection.XXssProtectionElement)this["xXssProtection"];
            }
            set
            {
                this["xXssProtection"] = value;
            }
        }
        #endregion

        #region Static Methods
        public static ShimConfiguration GetConfiguration()
        {
            ShimConfiguration config = (ShimConfiguration)System.Configuration.ConfigurationManager.GetSection("shim");
            setDefaultValues(config);
            setRelativeExcludePaths(config);
            return config;
        }
        #endregion

        #region Private Helpers
        private static void setDefaultValues(ShimConfiguration config)
        {
            if (config.Caching.Enabled)
            {
                //SET DEFAULTS FOR NO CACHE IF NOT DEFINED IN THE CONFIGURATION
                if (config.Caching.CacheControl.Enabled && config.Caching.CacheControl.Values.Count == 0)
                {
                    Caching.AddElementCacheControl noCache = new Caching.AddElementCacheControl();
                    noCache.Value = CacheControlType.NoCache;
                    Caching.AddElementCacheControl noStore = new Caching.AddElementCacheControl();
                    noStore.Value = CacheControlType.NoStore;
                    Caching.AddElementCacheControl revalidate = new Caching.AddElementCacheControl();
                    revalidate.Value = CacheControlType.MustRevalidate;
                    config.Caching.CacheControl.Values.Add(noCache);
                    config.Caching.CacheControl.Values.Add(noStore);
                    config.Caching.CacheControl.Values.Add(revalidate);
                }

                //SET DEFAULTS FOR PRAGMA IF NOT DEFINED IN THE CONFIGURATION
                if (config.Caching.Pragma.Enabled && config.Caching.Pragma.Values.Count == 0)
                {
                    Caching.AddElementCacheControl noCache = new Caching.AddElementCacheControl();
                    noCache.Value = CacheControlType.NoCache;
                    config.Caching.Pragma.Values.Add(noCache);
                }
            }

            //SET DEFAULTS FOR CSP IF NOT DEFINED IN THE CONFIGURATION
            if (config.ContentSecurityPolicy.Enabled)
            {
                //SET DEFAULT IF NOT DEFINED
                if (config.ContentSecurityPolicy.DefaultSource.Enabled && config.ContentSecurityPolicy.DefaultSource.Values.Count == 0)
                {
                    //Set the default sources to none
                    Csp.AddElementCspSource self = new Csp.AddElementCspSource();
                    self.Value = "self";
                    config.ContentSecurityPolicy.DefaultSource.Values.Add(self);
                }

                //DISABLE SCRIPT SOURCE IF NOT DEFINED
                if (config.ContentSecurityPolicy.ScriptSource.Enabled && config.ContentSecurityPolicy.ScriptSource.Values.Count == 0)
                    config.ContentSecurityPolicy.ScriptSource.Enabled = false;


                //DISABLE OBJECT SOURCE IF NOT DEFINED
                if (config.ContentSecurityPolicy.ObjectSource.Enabled && config.ContentSecurityPolicy.ObjectSource.Values.Count == 0)
                    config.ContentSecurityPolicy.ObjectSource.Enabled = false;

                //DISABLE STYLE SOURCE IF NOT DEFINED
                if (config.ContentSecurityPolicy.StyleSource.Enabled && config.ContentSecurityPolicy.StyleSource.Values.Count == 0)
                    config.ContentSecurityPolicy.StyleSource.Enabled = false;

                //DISABLE IMAGE SOURCE IF NOT DEFINED
                if (config.ContentSecurityPolicy.ImageSource.Enabled && config.ContentSecurityPolicy.ImageSource.Values.Count == 0)
                    config.ContentSecurityPolicy.ImageSource.Enabled = false;

                //DISABLE MEDIA SOURCE IF NOT DEFINED
                if (config.ContentSecurityPolicy.MediaSource.Enabled && config.ContentSecurityPolicy.MediaSource.Values.Count == 0)
                    config.ContentSecurityPolicy.MediaSource.Enabled = false;

                //DISABLE FRAME SOURCE IF NOT DEFINED
                if (config.ContentSecurityPolicy.FrameSource.Enabled && config.ContentSecurityPolicy.FrameSource.Values.Count == 0)
                    config.ContentSecurityPolicy.FrameSource.Enabled = false;

                //DISABLE FONT SOURCE IF NOT DEFINED
                if (config.ContentSecurityPolicy.FontSource.Enabled && config.ContentSecurityPolicy.FontSource.Values.Count == 0)
                    config.ContentSecurityPolicy.FontSource.Enabled = false;

                //DISABLE CONNECT SOURCE IF NOT DEFINED
                if (config.ContentSecurityPolicy.ConnectSource.Enabled && config.ContentSecurityPolicy.ConnectSource.Values.Count == 0)
                    config.ContentSecurityPolicy.ConnectSource.Enabled = false;

                //DISABLE REPORT URI IF NOT DEFINED
                if (config.ContentSecurityPolicy.ReportUri.Enabled && config.ContentSecurityPolicy.ReportUri.Values.Count == 0)
                    config.ContentSecurityPolicy.ReportUri.Enabled = false;
            }
        }
        private static void setRelativeExcludePaths(ShimConfiguration config)
        {
            if (config.Caching.Exclude.Values != null && config.Caching.Exclude.Values.Count > 0)
            {
                foreach (LocationElement e in config.Caching.Exclude.Values)
                    e.RelativePath = getRelativePath(e.Path);
            }

            if (config.ContentSecurityPolicy.Exclude.Values != null && config.ContentSecurityPolicy.Exclude.Values.Count > 0)
            {
                foreach (LocationElement e in config.ContentSecurityPolicy.Exclude.Values)
                    e.RelativePath = getRelativePath(e.Path);
            }

            if (config.XContentTypeOptions.Exclude.Values != null && config.XContentTypeOptions.Exclude.Values.Count > 0)
            {
                foreach (LocationElement e in config.XContentTypeOptions.Exclude.Values)
                    e.RelativePath = getRelativePath(e.Path);
            }

            if (config.XFrameOptions.Exclude.Values != null && config.XFrameOptions.Exclude.Values.Count > 0)
            {
                foreach (LocationElement e in config.XFrameOptions.Exclude.Values)
                    e.RelativePath = getRelativePath(e.Path);
            }

            if (config.XXssProtection.Exclude.Values != null && config.XXssProtection.Exclude.Values.Count > 0)
                foreach (LocationElement e in config.XXssProtection.Exclude.Values)
                    e.RelativePath = getRelativePath(e.Path);
        }
        /// <summary>
        /// Method ensures that the location paths entered in the shim configuration are normalized to /path/file, /file.css, /path/file, /path1/path2
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private static string getRelativePath(string path)
        {
            //If missing add it
            if (!path.StartsWith(_RELATIVE_PATH_KEY))
                path = string.Format("{0}{1}", _RELATIVE_PATH_KEY, path);            

            return path;
        }
        #endregion
    }
}
